package guru.merlin.core;

import guru.mercurio.common.RelativeApplicationAddress;
import guru.mercurio.common.PhoneAddress;

import java.io.File;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;


//  <route id="digitel">
//      <rule>envelope.getDestination().getClass()==PhoneAddress.class</rule>
//      <rule>envelope.getDestination().getDomain().equalsIgnoreCase("58")</rule>
//      <rule>envelope.getDestination().getPort().equalsIgnoreCase("412")</rule>
//  </route>
public class RoutingTableHandler {
    

    
    /*Es el documento xml routingTable.xml*/
    private Document xmlRoutingTable;
        

    /**
     * esto me lo obligo a poner el eclipse
     */
    private static final long serialVersionUID = 1L;
    
    
    private String routingTableFilePath;
    private SAXBuilder builder;
    private RuleEvaluator evaluator;
    
    /**
     * Obtiene el objeto Document que representa el archivo routingTable.xml
     * @param routingTableFilePath
     * @throws JDOMException
     */
    public RoutingTableHandler(String routingTableFilePath, RuleEvaluator evaluator) throws RuleSyntaxErrorException {
        this.routingTableFilePath=routingTableFilePath;
        this.evaluator=evaluator;
        // El constructor de documentos xml
        builder = new SAXBuilder();
        
        refreshRoutingTable();
    }
    
    public void refreshRoutingTable() throws RuleSyntaxErrorException {
        // Obtenemos el documento
        try{
        xmlRoutingTable = builder.build(new File(routingTableFilePath));
        }
        catch(Exception e){
            throw new RuleSyntaxErrorException("reason: "+e);
        }
    }
    
    /**
     * C:/home/lab/workspaces/m2/etc/RoutingTable-v2.xml
     * @param args
     */
    public static void main(String[] args) throws Exception {

        Envelope t1=new Envelope("Test envelope", new PhoneAddress(58,412,6210713), new RelativeApplicationAddress("digitel","CANAL"));
        Envelope o1=new Envelope("Test envelope", new RelativeApplicationAddress("digitel","22625"),new PhoneAddress(56,412,6210713));
        
        
        if (args.length == 0){
            System.err.println("Merlins routing table validation tool. v1.0.");
            System.err.println("syntax: <routing table filepath> [-t]");
            System.err.println("  -t     tests the routing table with: "+t1);
            System.err.println("  -o     tests the routing table with: "+o1);
            System.exit(1);
        }
        try {
            //RoutingTableHandler routingTable = new RoutingTableHandler(args[0]);
            RoutingTableHandler routingTable = new RoutingTableHandler(args[0], new RuleEvaluator());
            Envelope terminacion=t1;

            if(args.length==1){
                routingTable.check(terminacion);

            }
            else{
                System.out.println("Trying envelope:"+t1);
                Date begin=new Date();
                String routeId=routingTable.firstRute2Comply(t1);
                long elapsedTime = new Date().getTime() - begin.getTime();
                System.out.println("Primera RUTA que cumple: "+routeId+" (took "+elapsedTime+" miliseconds)");
            }
            
            

            
        }
        catch (Exception e) {
            System.err.println("error: "+e);
        }               
    }
    
    public String firstRute2Comply(Envelope envelope) throws RuleSyntaxErrorException{
        int cantReglas;
        int x;
        Element regla;
        
            //Obtenemos una lista con todas las rutas
            Iterator itrutas = getRoutes().iterator();
            
            //Iteramos por todas las rutas
            while (itrutas.hasNext()){
                //Obtenemos un elemento route
                Element ruta = (Element)itrutas.next();
                
                //Obtenemos el id de una ruta
                String idRuta = getRouteId(ruta); 
                //System.out.println(idRuta);
                
                //Obtenemos un lista con todas las reglas de una ruta
                List reglas = getRules(ruta);
                cantReglas = reglas.size();
                Iterator itreglas = reglas.iterator();
                
                //Iteramos por todas las reglas
                for (x=0;x<cantReglas;x++){ // while(itreglas.hasNext()){
                    // Obtenemos un elemento route
                    regla = (Element)itreglas.next();
                    
                    //Obtenemos el texto de una regla
                    String txtregla = getTextRule(regla);
                    //System.out.println(txtregla);
                    if(!(evaluator.evaluate(txtregla, envelope))){
                        break;
                    }
                }
                if(x==cantReglas){
                    return(idRuta);
                }
            }
            throw new RuleSyntaxErrorException("No RoutingTable rule matched");
    }    
    
    
    public void check(Envelope envelope){
        StringBuffer sb=new StringBuffer();
        Element regla;
        int i=0;
        int j=0;
            //Obtenemos una lista con todas las rutas
            Iterator itrutas = getRoutes().iterator();
            
            //Iteramos por todas las rutas
            while (itrutas.hasNext()){
                i++;
                //Obtenemos un elemento route
                Element ruta = (Element)itrutas.next();
                
                //Obtenemos el id de una ruta
                String idRuta = getRouteId(ruta); 
                //System.out.println(idRuta);
                
                //Obtenemos un lista con todas las reglas de una ruta
                List reglas = getRules(ruta);
                Iterator itreglas = reglas.iterator();
                
                //Iteramos por todas las reglas
                j=0;
                while(itreglas.hasNext()){
                    j++;
                    // Obtenemos un elemento route
                    regla = (Element)itreglas.next();
                    
                    //Obtenemos el texto de una regla
                    String txtregla = getTextRule(regla);
                    //System.out.println(txtregla);
                    try{
                        evaluator.evaluate(txtregla, envelope);
                        //System.
                    }
                    catch(RuleSyntaxErrorException e){
                        sb.append("<"+i+":"+idRuta+","+j+":"+txtregla+">: "+e.getMessage()+"\n");
                    }
                }
            }
            
            System.out.println("Result:");
            if(sb.length()==0){
                System.out.println("No error in routing table.");
            }
            else{
                System.out.println(sb.toString());
            }
        
    }

    
    
    
    // *********************************************************************************************
    /**
     * 
     * @return Lista de todos los elementos route
     */
    private List getRoutes(){
        Element rootToutingTable = xmlRoutingTable.getRootElement();
        return rootToutingTable.getChildren();
    }
    
    /**
     * El texto retornado por este mtodo, es el id del elemento route
     * @param route
     * @return El id de un elemento route. 
     */
    private String getRouteId(Element route){      
        return route.getAttributeValue("id");
    }        

    /**
     * 
     * @return Lista de todos los elementos rule de un elemento route
     */
    private List getRules(Element route){        
        return route.getChildren();
    }    
    
    
    /**
     * El texto retornado por este mtodo, es el que se le debe pasar a la funcin de verdad
     * @param rule
     * @return El texto de un elemento rule. 
     */
    private String getTextRule(Element rule){        
        return rule.getTextTrim();
    }

    /**
     * @return
     */
    /*
    private Document getXmlRoutingTable() {
        return xmlRoutingTable;
    }
    */
    
}
